import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import { LoadingOptionsResolved } from 'element-plus';
// import { ElLoading, ElMessage } from 'element-plus';
import cookies  from './cookie';

const getTokenAUTH = () => {
    return cookies.get("token")
}

const getWxToken = () => {
    return cookies.get("wx_access_token")
}

const pendingMap = new Map();

const LoadingInstance:LoadingInstanceType = {
    _target: null,
    _count: 0
};

interface LoadingInstanceType {
    _target:any,
    _count:number
}

interface custom_optionsType {
    repeat_request_cancel: boolean,
    loading: boolean,
    reduct_data_format: boolean,
    error_message_show: boolean,
    code_message_show: boolean
}

interface AxiosRequestConfig_1 extends AxiosRequestConfig{
    proxyStatus?:boolean
}

function myAxios(axiosConfig: AxiosRequestConfig_1, customOptions?: custom_optionsType, loadingOptions?: Partial<Omit<LoadingOptionsResolved, "target" | "parent"> & { target: string | HTMLElement; body: boolean; }> | undefined) {
    // console.log(axiosConfig)
    const service = axios.create({
        baseURL: axiosConfig.baseURL?axiosConfig.baseURL:!axiosConfig.proxyStatus?import.meta.env.VITE_BASE_API as string:"", // 设置统一的请求前缀
        timeout: 10000, // 设置统一的超时时长
    });

    // 自定义配置
    let custom_options: custom_optionsType = Object.assign({
        repeat_request_cancel: true, // 是否开启取消重复请求, 默认为 true
        loading: false, // 是否开启loading层效果, 默认为false
        reduct_data_format: true, // 是否开启简洁的数据结构响应, 默认为true
        error_message_show: true, // 是否开启接口错误信息展示,默认为true
        code_message_show: true, // 是否开启code不为200时的信息提示, 默认为false
    }, customOptions);

    // 请求拦截
    service.interceptors.request.use(
        (config: AxiosRequestConfig) => {
            removePending(config);
            custom_options.repeat_request_cancel && addPending(config);
            // 创建loading实例
            if (custom_options.loading) {
                LoadingInstance._count++;
                if (LoadingInstance._count === 1) {
                    LoadingInstance._target = ElLoading.service(loadingOptions);
                }
            }
            // console.log("获取token",getTokenAUTH())
            // 自动携带token
            if (getTokenAUTH() && typeof window !== "undefined") {
                config.headers!.Authorization = getTokenAUTH() as string;
            }

            if(getWxToken()){
                config.headers!.wx_access_token = getWxToken() as string;
            }

            return config;
        },
        error => {
            return Promise.reject(error);
        }
    );

    // 响应拦截
    service.interceptors.response.use(
        (response: AxiosResponse) => {
            removePending(response.config);
            custom_options.loading && closeLoading(custom_options); // 关闭loading
            if (custom_options.code_message_show && response.data && response.data.code !== 200) {
                ElMessage({
                    type: 'error',
                    message: response.data.message ? response.data.message : response.data.msg
                })
                return Promise.reject(response.data); // code不等于200, 页面具体逻辑就不执行了
            }

            return custom_options.reduct_data_format ? response.data : response;
        },
        error => {
            error.config && removePending(error.config);
            custom_options.loading && closeLoading(custom_options); // 关闭loading
            custom_options.error_message_show && httpErrorStatusHandle(error); // 处理错误状态码
            return Promise.reject(error); // 错误继续返回给到具体页面
        }
    );

    return service(axiosConfig)
}

export default myAxios;

/**
 * 处理异常
 * @param {*} error 
 */
function httpErrorStatusHandle(error: { message: string | string[]; response: { status: any; config: { url: any; }; }; }) {
    // 处理被取消的请求
    if (axios.isCancel(error)) return console.error('请求的重复请求：' + error.message);
    let message = '';
    console.log("处理被取消的请求", error)
    if (error && error.response) {
        switch (error.response.status) {
            case 302: message = '接口重定向了！'; break;
            case 400: message = '参数不正确！'; break;
            case 401: message = '您未登录，或者登录已经超时，请先登录！'; break;
            case 403: message = '您没有权限操作！'; break;
            case 404: message = `请求地址出错: ${error.response.config.url}`; break; // 在正确域名下
            case 408: message = '请求超时！'; break;
            case 409: message = '系统已存在相同数据！'; break;
            case 500: message = '服务器内部错误！'; break;
            case 501: message = '服务未实现！'; break;
            case 502: message = '网关错误！'; break;
            case 503: message = '服务不可用！'; break;
            case 504: message = '服务暂时无法访问，请稍后再试！'; break;
            case 505: message = 'HTTP版本不受支持！'; break;
            default: message = '异常问题，请联系管理员！'; break
        }
    }
    if (error.message.includes('timeout')) message = '网络请求超时！';
    if (error.message.includes('Network')) message = window.navigator.onLine ? '服务端异常！' : '您断网了！';

    ElMessage({
        type: 'error',
        message
    })
}

/**
 * 关闭Loading层实例
 * @param {*} _options 
 */
function closeLoading(_options: custom_optionsType) {
    if (_options.loading && LoadingInstance._count > 0) LoadingInstance._count--;
    if (LoadingInstance._count === 0) {
        LoadingInstance._target.close();
        LoadingInstance._target = null;
    }
}

/**
 * 储存每个请求的唯一cancel回调, 以此为标识
 * @param {*} config 
 */
function addPending(config: AxiosRequestConfig) {
    const pendingKey = getPendingKey(config);
    config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => {
        if (!pendingMap.has(pendingKey)) {
            pendingMap.set(pendingKey, cancel);
        }
    });
}

/**
 * 删除重复的请求
 * @param {*} config 
 */
function removePending(config: AxiosRequestConfig | AxiosResponse) {
    const pendingKey = getPendingKey(config);
    if (pendingMap.has(pendingKey)) {
        const cancelToken = pendingMap.get(pendingKey);
        // 如你不明白此处为什么需要传递pendingKey可以看文章下方的补丁解释
        cancelToken(pendingKey);
        pendingMap.delete(pendingKey);
    }
}

/**
 * 生成唯一的每个请求的唯一key
 * @param {*} config 
 * @returns 
 */
function getPendingKey(config: AxiosRequestConfig<any>) {
    let { url, method, params, data } = config;
    if (typeof data === 'string') data = JSON.parse(data); // response里面返回的config.data是个字符串对象
    return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&');
}
